TLS All The Things! Perfect 100 SSL-Labs Score Revisited

Want to have a "perfect" ssl-labs score? An A+ 100/100/100/100 on SSLLabs that is? Here is my config file/example and adjust it for your own purposes. My last version was tls 1.2 only. This version allows for a non-compliant tls 1.3 implementation due to removal of all 128-bit ciphers for key-exchange. (hence the "perfect"...)

Code-snippets for Nginx with TLS1.3 and OpenSSL 1.1.1

# Configuration by Angelique Dawnbringer
# HTTPS server
server {
    listen 1.2.3.4:443;
    server_name example.net;

    access_log                  /var/log/nginx/example.access.log;
    error_log                   /var/log/nginx/example.error.log;

    server_tokens off;

    ssl_certificate             /etc/nginx/ssl/example.net/cert.crt;
    ssl_certificate_key         /etc/nginx/ssl/example.net/cert.key;

    ssl                         on;
    ssl_session_cache           builtin:1000  shared:SSL:2m;
    ssl_session_timeout         5m;
    ssl_protocols               TLSv1.3 TLSv1.2;
    ssl_prefer_server_ciphers   on;
    ssl_ecdh_curve              secp521r1:secp384r1; 

    # If you want to use 0-RTT uncomment the following line. 
    # However read https://tools.ietf.org/html/rfc8470 first.
    #ssl_early_data             on;
    

    # Cipher Support-block
    # Support for most browsers and systems but including some CBC-weak ciphers
    # openssl ciphers  'EECDH+AESGCM:EDH+AESGCM:!DH:!RSA:!AES128'
    ssl_ciphers                TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS_AES_256_GCM_SHA384:TLS-AES-256-GCM-SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS-CHACHA20-POLY1305-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA;

    # Use this for non-CBC (no "weak" ciphers)
    #ssl_ciphers               TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS_AES_256_GCM_SHA384:TLS-AES-256-GCM-SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS-CHACHA20-POLY1305-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;

    # openssl ciphers  'EECDH+AESGCM:EDH+AESGCM:!DHE:!AES128'
    #ssl_ciphers               TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384

    ssl_stapling                on;
    ssl_stapling_verify         on;

    add_header Strict-Transport-Security "max-age=31536000;";
    # I am a big fan of ubiquitous encryption and HSTS ;) Go Preload! https://hstspreload.org/
    #add_header                  Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    resolver 8.8.8.8 8.8.4.4;

    #Some document root#
}

The cipher blocks include issues with TLS1.3/NGINX version handling. I added the openssl code blocks above for you to filter those you want/need/are available. You can generate "cleaner" versions with these. You could also take: ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; and remove AES128 and DHE versions.

TLS 1.3 specifics

Nginx and others do not support OpenSSL TLS1.3 cipher configuration. This has to do with RFC 8446. As of 1.1.1-c you can specify this and other settings in the openssl.cnf files. However, this does make you break the official mandatory cipher-list. The following ciphers are mandatory:

angelique@dawnbringer:/usr/local/src$ openssl ciphers -s -v | grep 1.3
TLS_AES_256_GCM_SHA384  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(128) Mac=AEAD

In our attempt to become 100/100/100/100, we will break compliance and configure OpenSSL to not use TLS_AES_128_GCM_SHA256. In my use-cases, i tend to prioritize CHACHA20-poly1305 over AES for mobile support and such. In the past you needed to patch openssl and nginx for this. This is not the case anymore and you can very easily achieve this with the following block configuration change.

...

[default_conf]
ssl_conf = ssl_sect

[ssl_sect]
system_default = system_default_sect

[system_default_sect]
MinProtocol = TLSv1.2
CipherString = DEFAULT@SECLEVEL=2
Ciphersuites = TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
Options = ServerPreference,PrioritizeChaCha

...

This will state to your OS that the minimum TLS version used is TLS1.2 and the Ciphersuites to use ar the ones specified. Please note that I have only specified TLS1.3 suites. If you need TLS1.2 support, do add "some" of them. You will now have a full 100/100/100/100 configuration using 256 bit encryption. (and a very strong key-exchange ;))

Examples or Stuck?

My website uses Cloudflare as a proxy and waf but scores lower than that. But all my internal hosts use this profile since I only want to use TLS1.2 or newer to begin with. This domain (oceandns) does have the above profile running: Check SSL Labs here. For an ECC/ECDSA enabled site check: here

Are you stuck on 90% key exchange? Then you most likely haven't changed or hard coded the SSL ECDH Curve setting. The standard value for Nginx is:

ssl_ecdh_curve              prime256v1

Which will give you an equivalent of 3k instead of 4k. Change or set this value to:

ssl_ecdh_curve              secp384r1;

Are you not able to get ECDSA to work? Then you most likely have not added an additional line and non-rsa -> ecc certificate to your configuration. For ECDSA to work, you need a ECC private key/and public key certificate.

Author: Angelique Dawnbringer Published: 2019-10-27 20:24:17 Keywords:
  • SSL
  • All The Things
  • SSL Labs
  • Perfect Score
  • PCI-DSS
Modified: 2019-10-28 10:22:40